import  { AxiosInstance } from 'axios'
import { HttpCacheUtil } from './cache/http_cache_util';
import { bdjfLog } from './log_util';
import { copyRes, createRes, ResponseConstructor, ResponseImpl } from './response';
import { RESPONSE_CACHE_ERROR, RESPONSE_CATCH_ERROR, RESPONSE_CODE_NO_DATA, RESPONSE_CODE_NO_RESPONSE } from './system_constant';
import { BdjfRequest, BdjfResponse, HttpConfig } from './type';


export class BdjfHttp{


    axiosInstance:AxiosInstance;

    config:HttpConfig;

    cacheUtil:HttpCacheUtil|undefined;

    constructor(axiosInstance:AxiosInstance,config:HttpConfig){
        this.axiosInstance = axiosInstance;
        this.config = config;
    }

    private createErrorRes(resConfig:ResponseConstructor){
        return new ResponseImpl({
            ...resConfig,
            successCode:this.config.successCode
        });
    }

    public request(requestData:BdjfRequest):Promise<BdjfResponse>{
        if(requestData.requestOption.needCache){
            return this.cacheRequest(requestData);
        }else{
            return this.sendRequest(requestData)
        }
    }

    private log(...data:any[]){
        bdjfLog(this.config.openLog,data);
    }

    private cacheRequest(requestData:BdjfRequest): Promise<BdjfResponse> {

        let key: string | Record<string,any>;

        const url = requestData.requestOption.url;

        const log = this.log;

        if(!this.cacheUtil){
            this.cacheUtil = new HttpCacheUtil(this.config);
        }

        const cacheUtil = this.cacheUtil;

        const bindCacheKey = requestData.cacheOption?.bindCacheKey;
        if (bindCacheKey && bindCacheKey.length > 0) {
            // data 一般为post中传输数据
            // params 一般为 get 中传输数据
            const params = requestData.requestOption.data||requestData.requestOption.params;
            key = cacheUtil.generateKey(params, bindCacheKey);
        } else {
            key = url;
        }

        try {
            const cacheData = cacheUtil.getCacheDataByKey(url, key);
            if (cacheData&&cacheData.success) {
                log('---从存储中获取数据---', cacheData);
                return Promise.resolve(cacheData);
            }
        } catch (error:any) {
            const errorRes = this.createErrorRes({
                code:RESPONSE_CACHE_ERROR,
                msg:error.message,
                data:error
            });
            return Promise.resolve(errorRes);
        }
        

        return new Promise( (resolve, reject)=> {
            this.sendRequest(requestData)
                .then(res => {
                    log('---从网络获得数据---', res)
                    if (res.success) {
                        if (res.data instanceof Array) {
                            if (res.data.length > 0) {
                                cacheUtil.setCacheData(url, key, copyRes(res))
                            }
                        } else {
                            if (res.data) {
                                cacheUtil.setCacheData(url, key, copyRes(res))
                            }
                        }
                    }
                    resolve(res);
                }).catch(e => {
                    reject(e)
                })
        })
    }

    private sendRequest(requestData:BdjfRequest):Promise<BdjfResponse>{
        return new Promise((resolve,reject)=>{

            const config = this.config;

            const createErrorRes = this.createErrorRes;

            if(!requestData.requestOption.hideLoading){
                config.showLoading(requestData);
            }

            this.axiosInstance(requestData.requestOption).then(res=>{
                let responseData:BdjfResponse;

                if(res === undefined){
                    responseData = createErrorRes({
                        code:RESPONSE_CODE_NO_RESPONSE,
                        msg:'no response',
                    });
                    config.showError(responseData,requestData);
                }else{
                    if(res.status === 200){
                        const data: any = res.data;
                        if(data){
                            responseData = createRes(config.successCode,data,requestData.requestOption.attributeMapping||config.attributeMapping);
                            if(responseData.success){
                                if(this.cacheUtil&&requestData.cacheOption?.conflictCacheUrl){
                                    this.cacheUtil.clearObsoleteCacheData(requestData.conflictCacheUrl());
                                }
                            }else{
                                 if(!requestData.requestOption.hideWarn){
                                    config.showWarn(responseData,requestData);
                                 }
                            }
                        }else{
                            responseData = createErrorRes({
                                code:RESPONSE_CODE_NO_DATA,
                                msg:'no data',
                            });
                            config.showError(responseData,requestData);
                        }
                    }else{
                        responseData = createErrorRes({
                            code:res.status,
                            msg:'no data',
                        });
                        config.showError(responseData,requestData);
                    }
                }
                if(!requestData.requestOption.hideLoading){
                    this.config.hideLoading(responseData,requestData);
                }
                resolve(responseData);
            }).catch(e=>{
                const errorResponse = createErrorRes({
                    code:RESPONSE_CATCH_ERROR,
                    msg:e.message,
                    data:e
                });
                if(!requestData.requestOption.hideLoading){
                    config.hideLoading(errorResponse,requestData);
                }
                if(!requestData.requestOption.hideError){
                    config.showError(errorResponse,requestData);
                }
                if(requestData.requestOption.rejectError){
                    reject(errorResponse);
                }else{
                    resolve(errorResponse);
                }
            })
        })
    }

}